﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using AForge.Imaging.Filters;
using AForge.Imaging;
using AForge;
//Sebastian Hesse
namespace Bildverarbeitung
{
    class HesseSebastianDynamicFilter : BaseInPlacePartialFilter
    {
        public enum Method
        {
            AVERANGE,
            MEDIAN,
            FREQUENZY
        };

        private Dictionary<PixelFormat, PixelFormat> formatTranslations = new Dictionary<PixelFormat, PixelFormat>();
        private Bitmap[] imgs;       
        private int abw;
        private Method value;

        public override Dictionary<PixelFormat, PixelFormat> FormatTranslations
        {
            get { return formatTranslations; }
        }       

        public int Abw
        {
            get { return abw; }
            set { abw = value; }
        }

        public HesseSebastianDynamicFilter(Bitmap[] bList, Method value) 
        {
            formatTranslations[PixelFormat.Format8bppIndexed] = PixelFormat.Format8bppIndexed;
            formatTranslations[PixelFormat.Format24bppRgb]    = PixelFormat.Format24bppRgb;
            formatTranslations[PixelFormat.Format32bppRgb]    = PixelFormat.Format32bppRgb;
            formatTranslations[PixelFormat.Format32bppArgb]   = PixelFormat.Format32bppArgb;
            this.imgs = bList;             
            this.value = value;
            abw = 10;
        }

        protected override unsafe void ProcessFilter(UnmanagedImage dstimg, Rectangle rect)
        {                         
            byte* dst = (byte*)dstimg.ImageData.ToPointer();

            UnmanagedImage[] uImgs = new UnmanagedImage[imgs.Length];
            byte*[] srcPtr = new byte*[uImgs.Length];
           
            //Convert managed Images to Unmanaged Images and init the pointer Array for computation
            for (int i = 0; i < imgs.Length; i++)
            {
                uImgs[i] = UnmanagedImage.FromManagedImage(imgs[i]);
                srcPtr[i] = (byte*)uImgs[i].ImageData.ToPointer();
            }

            int pixelSize = (dstimg.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;              

            int startX = rect.Left;
            int startY = rect.Top;
            int stopX = startX + rect.Width;
            int stopY = startY + rect.Height;

            int srcStride = uImgs[0].Stride;
            int dstStride = dstimg.Stride;

            int srcOffset = srcStride - rect.Width * pixelSize;
            int dstOffset = dstStride - rect.Width * pixelSize;



            // allign pointers to the first pixel to process
            dst += (startY * dstimg.Stride + startX * pixelSize);
            for (int i = 0; i < srcPtr.Length; i++)
            {
                srcPtr[i] += (startY * srcStride + rect.Left * pixelSize);
            }

            int r, g, b;
            int length = srcPtr.Length;
            if (value == Method.AVERANGE)
            {
                for (int y = startY; y < stopY; y++)
                {
                    for (int x = startX; x < stopX; x++)
                    {
                        r = 0;
                        g = 0;
                        b = 0;
                        for (int i = 0; i < length; i++)
                        {
                            r += srcPtr[i][RGB.R];
                            g += srcPtr[i][RGB.G];
                            b += srcPtr[i][RGB.B];
                            srcPtr[i] += pixelSize;
                        }
                        dst[RGB.R] = (byte)(r / length);
                        dst[RGB.G] = (byte)(g / length);
                        dst[RGB.B] = (byte)(b / length);
                        dst += pixelSize;
                    }
                    for (int i = 0; i < length; i++)
                    {
                        srcPtr[i] += srcOffset;
                    }
                    dst += dstOffset;
                }
            }
            else if(value == Method.MEDIAN)
            {
               
                byte[] rAr = new byte[length];
                byte[] gAr = new byte[length];
                byte[] bAr = new byte[length];
                int median = length >> 1;

                for (int y = startY; y < stopY; y++)
                {
                    for (int x = startX; x < stopX; x++)
                    {
                        for (int i = 0; i < length; i++)
                        {
                            rAr[i] = srcPtr[i][RGB.R];
                            gAr[i] = srcPtr[i][RGB.G];
                            bAr[i] = srcPtr[i][RGB.B];
                            srcPtr[i] += pixelSize;
                        }
                        Array.Sort(rAr, 0, length);
                        Array.Sort(gAr, 0, length);
                        Array.Sort(bAr, 0, length);

                        dst[RGB.R] = rAr[median];
                        dst[RGB.G] = gAr[median];
                        dst[RGB.B] = bAr[median];
                        dst += pixelSize;
                    }
                    for (int i = 0; i < length; i++)
                    {
                        srcPtr[i] += srcOffset;
                    }
                    dst += dstOffset;
                }
            }
            else
            {
                int[,] rgbArr;
                int size;
                int mostUsedColor;
                int colorIsFound;                

                for (int y = startY; y < stopY; y++)
                {
                    for (int x = startX; x < stopX; x++)
                    {
                        r = 0;
                        g = 0;
                        b = 0;
                        rgbArr = new int[length, 4];
                        size = 0;
                        for (int i = 0; i < length; i++)
                        {
                            colorIsFound = -1;
                            r = srcPtr[i][RGB.R];
                            g = srcPtr[i][RGB.G];
                            b = srcPtr[i][RGB.B];

                            if (rgbArr[0, 3] == 0)
                            {
                                rgbArr[0, 0] = r;
                                rgbArr[0, 1] = g;
                                rgbArr[0, 2] = b;
                                rgbArr[0, 3] += 1;
                                size++;
                            }
                            else
                            {
                                for (int j = 0; j < size; j++)
                                {
                                    if (((r - abw) < rgbArr[j, 0]) && ((r + abw) > rgbArr[j, 0])) 
                                    {
                                        if (((g - abw) < rgbArr[j, 1]) && ((g + abw) > rgbArr[j, 1]))  
                                        {
                                            if (((b - abw) < rgbArr[j, 2]) && ((b + abw) > rgbArr[j, 2])) 
                                            {
                                                colorIsFound = j;
                                                rgbArr[colorIsFound, 3] += 1;
                                                break;
                                            }
                                        }
                                    }
                                }
                                if (colorIsFound == -1)
                                {
                                    rgbArr[size, 0] = r;
                                    rgbArr[size, 1] = g;
                                    rgbArr[size, 2] = b;
                                    rgbArr[size, 3] += 1;
                                    size++;
                                }
                            }
                            srcPtr[i] += pixelSize;
                        }                    

                        mostUsedColor = 0;                
                        for (int j = 1; j < size; j++)
                        {
                            if (rgbArr[mostUsedColor, 3] < rgbArr[j, 3])
                            {                         
                                mostUsedColor = j;
                            }
                        }
                        dst[RGB.R] = (byte)(rgbArr[mostUsedColor, 0]);
                        dst[RGB.G] = (byte)(rgbArr[mostUsedColor, 1]);
                        dst[RGB.B] = (byte)(rgbArr[mostUsedColor, 2]);
                        dst += pixelSize;
                    }
                    for (int i = 0; i < length; i++)
                    {
                        srcPtr[i] += srcOffset;
                    }
                    dst += dstOffset;
                }
            }
        }
    }
}
